﻿// -----------------------------
// Copyright © Kodi Studios 2012
// -----------------------------

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Collections.Generic;

namespace Kodi.Picturethrill
{
    public class Log
    {
        #region Private Fields
        private static string filePath = Path.Combine(Settings.CurrentProcessDirectoryPath, Process.GetCurrentProcess().ProcessName + "Log.txt");
        private static string filePathPrevious = Path.Combine(Settings.CurrentProcessDirectoryPath, Process.GetCurrentProcess().ProcessName + "LogPrevious.txt");
        private static long MaxFileSize = 200 * 1024; // 200KB, in bytes
        private static object logLock = new object();
        private static Dictionary<int, int> threadIndentHash = new Dictionary<int, int>();
        #endregion

        #region Constructors
        static Log()
        {
            try
            {
                // static constructor
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }
            }
            catch (Exception)
            {
            }
        }
        #endregion

        #region Public Methods
        public static void BeginGroup(string message)
        {
            lock (logLock)
            {
                int threadId = Thread.CurrentThread.ManagedThreadId;

                if (!threadIndentHash.ContainsKey(threadId))
                {
                    threadIndentHash.Add(threadId, 0);
                }

                Comment(message);
                threadIndentHash[threadId]++;
            }
        }

        public static void EndGroup(string message)
        {
            lock (logLock)
            {
                int threadId = Thread.CurrentThread.ManagedThreadId;

                if (!threadIndentHash.ContainsKey(threadId))
                {
                    threadIndentHash.Add(threadId, 0);
                }

                threadIndentHash[threadId]--;

                if (threadIndentHash[threadId] < 0)
                {
                    threadIndentHash[threadId] = 0;
                }
            }
        }

        public static void Comment(string message)
        {
            lock (logLock)
            {
                try
                {
                    using (StreamWriter streamWriter = new StreamWriter(filePath, append: true))
                    {
                        FileInfo fileInfo = new FileInfo(filePath);
                        if (fileInfo.Exists)
                        {
                            if (fileInfo.Length > MaxFileSize)
                            {
                                File.Copy(filePath, filePathPrevious);
                                File.Delete(filePath);
                            }
                        }

                        // Write to log
                        int threadId = Thread.CurrentThread.ManagedThreadId;
                        if (!threadIndentHash.ContainsKey(threadId))
                        {
                            threadIndentHash.Add(threadId, 0);
                        }

                        string spacer = new string(' ', threadIndentHash[threadId] * 4);
                        streamWriter.WriteLine(DateTime.Now + " " + threadId + " " + spacer + message);
                    }
                }
                catch (Exception)
                {
                    // TODO: Log into system log
                }
            }
        }

        public static IDisposable Group(string groupName)
        {
            return new CloseGroup(groupName);
        }

        public static IDisposable Group()
        {
            StackTrace stackTrace = new StackTrace();
            string methodName;

            if (stackTrace.FrameCount > 1)
            {
                // Calling method name.
                methodName = stackTrace.GetFrame(1).GetMethod().Name;
            }
            else
            {
                // Current method name if calling method is unavailable.
                methodName = stackTrace.GetFrame(0).GetMethod().Name;
            }

            return Group(methodName);
        }
        #endregion

        private class CloseGroup : IDisposable
        {
            private string groupName;

            public CloseGroup(string groupName)
            {
                this.groupName = groupName;
                BeginGroup(this.groupName);
            }

            public void Dispose()
            {
                EndGroup(this.groupName + " Done");
            }
        }
    }
}